package com.google.gwt.it2tool.client;

import java.util.HashMap;
import java.util.List;

import com.google.code.p.gwtchismes.client.GWTCWait;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.it2tool.client.widget.AddFactoryPanel;
import com.google.gwt.it2tool.client.widget.AddFactoryPanel.IFactoryAddedHandler;
import com.google.gwt.it2tool.client.widget.CustomFactoryOrderPanel;
import com.google.gwt.it2tool.client.widget.CustomFactoryOrderPanel.FactoryOrderRequestHandler;
import com.google.gwt.it2tool.client.widget.FactoryDetailsPanel;
import com.google.gwt.it2tool.client.widget.FactoryDetailsPopupPanel;
import com.google.gwt.it2tool.client.widget.FactoryFlexTable;
import com.google.gwt.it2tool.client.widget.FactoryFlexTable.IFactoryEventsHandler;
import com.google.gwt.it2tool.client.widget.LoginPanel;
import com.google.gwt.it2tool.client.widget.MassProductionPanel;
import com.google.gwt.it2tool.client.widget.MassProductionPanel.IMassProductionChangedHandler;
import com.google.gwt.it2tool.client.widget.ProductLabel.IProductClickedHandler;
import com.google.gwt.it2tool.client.widget.RecipePopupPanel;
import com.google.gwt.it2tool.client.widget.RecipePopupPanel.IRecipeHandler;
import com.google.gwt.it2tool.client.widget.RessourceNeedsFlexTable;
import com.google.gwt.it2tool.client.widget.StatusPanel;
import com.google.gwt.it2tool.client.widget.SummaryFlexTable;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.user.client.ui.VerticalPanel;



public class IT2Tool implements EntryPoint {

	//The Root Panel
	private VerticalPanel mainPanel = new VerticalPanel();
	
	//Contains everything except sign out and bug link
	private TabPanel tabPanel = new TabPanel();
	private HorizontalPanel overviewPanel = new HorizontalPanel();
	private FactoryFlexTable factoriesFlexTable;
	private FactoryFlexTable factoriesFlexTableCollapsed = new FactoryFlexTable(true);
	private FactoryFlexTable factoriesFlexTableExpanded = new FactoryFlexTable(false);
	
	//Right Panel and stuff in it
	private VerticalPanel rightPanel = new VerticalPanel();
	private DisclosurePanel factoryOptionsPanel = new DisclosurePanel("Fabrikoptionen");
	private DisclosurePanel outputSummaryPanel = new DisclosurePanel("Tagesproduktion");
	private DisclosurePanel ressourceSummaryPanel = new DisclosurePanel("Ressourcenbedarf");
	private StatusPanel statusPanel = new StatusPanel();
	private AddFactoryPanel addFactoryPanel = new AddFactoryPanel();
	private MassProductionPanel massProductionPanel = new MassProductionPanel();
	private CustomFactoryOrderPanel customFactoryOrderPanel = new CustomFactoryOrderPanel();
	private SummaryFlexTable summaryFlexTable = new SummaryFlexTable();
	private RessourceNeedsFlexTable ressouceFlexTable = new RessourceNeedsFlexTable();
	
	//Is nowhere located in particular as it is a popup
	private RecipePopupPanel recipePopupPanel = new RecipePopupPanel();	

	//Data Model
	private ClientDataCache dataCache;
	private HashMap<String,Factory>  fabs = new HashMap<String,Factory> ();
	private HashMap<String,Recipe>  recipes = new HashMap<String,Recipe> ();
	private HashMap<String,ProductionPrototype>  prototypes = new HashMap<String,ProductionPrototype> ();
	private int massProdLevel = 0;
	private LoginInfo loginInfo;
	
	//To be shown if not logged in
	private LoginPanel loginPanel;
	
	
	//Kind of a Footer
	private HorizontalPanel linkFooterPanel = new HorizontalPanel();
	private Anchor signOutLink = new Anchor("Sign Out");
	private Anchor reportBugLink = new Anchor("Du hast einen Fehler gefunden? - Melde ihn hier!");

	private GWTCWait waitLoadPersonalDataDialog;

	private final FactoryServiceAsync factoryService = GWT.create(FactoryService.class);

	/**
	 * Entry point method.
	 */
	public void onModuleLoad() {
		
		dataCache = ClientDataCache.getInstance();

		// Create a wait widget
		final GWTCWait waitLoginCheck = new GWTCWait();

		waitLoginCheck.setMessage("LoginStatus wird überprüft...");
		waitLoginCheck.center();

		// Check login status using login service.
		LoginServiceAsync loginService = GWT.create(LoginService.class);
		loginService.login(GWT.getHostPageBaseURL(), new AsyncCallback<LoginInfo>() {
			public void onFailure(Throwable error) {
				waitLoginCheck.hide();
				handleError(error);
			}

			public void onSuccess(LoginInfo result) {
				waitLoginCheck.hide();
				loginInfo = result;
				if(result.isLoggedIn()) {
					loadRecipes();
				} else {
					loadLogin(result);
				}
			}
		});
	}

	private void loadLogin(LoginInfo info) {
		// Assemble login panel.
		loginPanel =  new LoginPanel(info);
		RootPanel.get("fabList").add(loginPanel);
	}

	private void initPanelsAndLoadUserData() {	

		// Create a wait widget
		waitLoadPersonalDataDialog = new GWTCWait();   
		waitLoadPersonalDataDialog.setMessage("Deine Daten werden geladen...");
		waitLoadPersonalDataDialog.center();

		// Set up sign out hyperlink.
		initLinkFooter();		

		// Create table for Factory data.
		initFactoriesFlexTable();

		initRecipePopupPanel();	

		// Assemble Right Panel
		initRightPanel();
		
		overviewPanel.add(factoriesFlexTable);
		overviewPanel.add(rightPanel);

		//Assemble Main Panel
		mainPanel.add(tabPanel);
		mainPanel.add(linkFooterPanel);
		
		//Assemble TabPanel
		tabPanel.add(overviewPanel, "Übersicht");
		tabPanel.addStyleName("tabPanel");
		tabPanel.setAnimationEnabled(true);

		// Associate the Main panel with the HTML host page.
		RootPanel.get("fabList").add(mainPanel);
		tabPanel.selectTab(0);
		
		
		loadMassProductionLvl();		
	}

	private void initFactoriesFlexTable(){
		factoriesFlexTableCollapsed.addIFactoryEventHandler(factoryEventsHandler);
		factoriesFlexTableCollapsed.addProductClickedHandler(productClickHandler);
		
		factoriesFlexTableExpanded.addIFactoryEventHandler(factoryEventsHandler);
		factoriesFlexTableExpanded.addProductClickedHandler(productClickHandler);
		factoriesFlexTable = factoriesFlexTableCollapsed;
	}
	
	private void initLinkFooter(){
		signOutLink.setHref(loginInfo.getLogoutUrl());
		signOutLink.addStyleName("signOutLink");
		reportBugLink.setHref("http://code.google.com/p/it2tool/issues/list");
		reportBugLink.addStyleName("signOutLink");
		reportBugLink.setTarget("_blank");
		linkFooterPanel.add(signOutLink);
		linkFooterPanel.add(reportBugLink);
	}
	
	private void initRessourceNeedsTable(){
		ressouceFlexTable.addProductClickedHandler(productClickHandler);
	}
	
	private void initSummaryFlexTable(){
		summaryFlexTable.addProductClickedHandler(productClickHandler);
	}

	private void initMassProductionPanel(){
		massProductionPanel.addMassProductionChangedHandler(new IMassProductionChangedHandler() {			
			public void onMassProductionChanged(int newLvl) {
				massProdLevel = newLvl;
				dataCache.setMassProdLevel(massProdLevel);
				storeMassProduction(newLvl);				
			}
		});
	}
	
	private void initFactoryOptionsPanel(){
		
		initAddFactoryPanel();
		initMassProductionPanel();
		initCustomFactoryOrderPanel();
		
		factoryOptionsPanel.addStyleName("factoryOptionsPanel");
		factoryOptionsPanel.setAnimationEnabled(true);
		VerticalPanel content = new VerticalPanel();
		content.add(addFactoryPanel);
		content.add(massProductionPanel);
		content.add(customFactoryOrderPanel);
		factoryOptionsPanel.setContent(content);
	}

	private void initAddFactoryPanel(){
		addFactoryPanel.addFactoryAddedHandler(new IFactoryAddedHandler() {			
			public void onFactoryAdded(String name) {
				factoriesFlexTable.addFactory(name);
				storeFactory(new Factory(name));
			}
		});	
	}
	
	private void initCustomFactoryOrderPanel(){
		customFactoryOrderPanel.addFactoryOrderRequestHandler(new FactoryOrderRequestHandler() {			
			@Override
			public void onSaveFactoryOrder() {
				factoriesFlexTable.onHideAndSaveCustomOrder();
				storeCustomFactoryOrder();
			}	
			
			@Override
			public void onChangeFactoryOrder() {
				factoriesFlexTable.onShowCustomOrder();				
			}
		});
	}

	private void initRecipePopupPanel(){
		recipePopupPanel.addRecipeHandler(new IRecipeHandler() {			
			public void onRecipeError(String error) {
				statusPanel.addErrorMessage(error);				
			}			
			public void onRecipeAdded(Recipe recipe) {
				storeRecipe(recipe);
			}
		});
	}

	private void initRightPanel(){
		
		initFactoryOptionsPanel();		
		initRessourceNeedsTable();
		initSummaryFlexTable();	
		
		outputSummaryPanel.add(summaryFlexTable);
		outputSummaryPanel.setAnimationEnabled(true);
		outputSummaryPanel.setOpen(true);
		outputSummaryPanel.addStyleName("outputSummaryPanel");
		
		ressourceSummaryPanel.add(ressouceFlexTable);
		ressourceSummaryPanel.addStyleName("ressourceSummaryPanel");
		ressourceSummaryPanel.setAnimationEnabled(true);
		
		rightPanel.add(statusPanel);
		rightPanel.add(factoryOptionsPanel);
		rightPanel.add(outputSummaryPanel);
		rightPanel.add(ressourceSummaryPanel);
		rightPanel.addStyleName("rightPanel");
	}

	private void loadRecipes(){
		// Create a wait widget
		final GWTCWait waitLoadRecipes = new GWTCWait();

		waitLoadRecipes.setMessage("Rezepte werden geladen...");
		waitLoadRecipes.center();

		factoryService.getRecipies(new AsyncCallback<List<Recipe>>() {
			public void onFailure(Throwable caught) {
				handleError(caught);
				waitLoadRecipes.hide();
			}

			public void onSuccess(List<Recipe> result) {
				for(Recipe r: result){
					recipes.put(r.getName(), r);;
				}
				dataCache.setRecipes(recipes);
				waitLoadRecipes.hide();
				loadProductionPrototypes();
			}
		});
	}
	
	private void loadProductionPrototypes(){
		// Create a wait widget
		final GWTCWait waitLoadPrototypes = new GWTCWait();

		waitLoadPrototypes.setMessage("Produktionszeiten werden geladen...");
		waitLoadPrototypes.center();

		factoryService.getProductionPrototypes(new AsyncCallback<List<ProductionPrototype>>() {
			public void onFailure(Throwable caught) {
				handleError(caught);
				waitLoadPrototypes.hide();
			}

			public void onSuccess(List<ProductionPrototype> result) {
				for(ProductionPrototype p: result){
					prototypes.put(p.getName(), p);	
				}
				dataCache.setPrototypes(prototypes);
				waitLoadPrototypes.hide();
				initPanelsAndLoadUserData();
			}
		});
	}

	private void loadFactories() {
		factoryService.getFactories(new AsyncCallback<List<Factory>>() {
			public void onFailure(Throwable caught) {
				handleError(caught);
				waitLoadPersonalDataDialog.hide();
			}

			public void onSuccess(List<Factory> result) {
				for(Factory f: result){
					fabs.put(f.getId(), f);
					tabPanel.add(new FactoryDetailsPanel(f),f.getName());
				}
				waitLoadPersonalDataDialog.hide();
				loadCustomFactoryOrder();
			}
		});
	}
	
	private void loadCustomFactoryOrder(){
		
		// Create a wait widget
		final GWTCWait waitLoadCustomFactoryOrder = new GWTCWait();

		waitLoadCustomFactoryOrder.setMessage("Benutzerdefinierte Fabrikreihenfolge wird geladen...");
		waitLoadCustomFactoryOrder.center();
		
		factoryService.getCustomFactoryOrder(new AsyncCallback<CustomFactoryOrder>() {
			
			@Override
			public void onSuccess(CustomFactoryOrder result) {
				waitLoadCustomFactoryOrder.hide();
				dataCache.setFactories(fabs);
				dataCache.setCustomFactoryOrder(result);
				if(result.getOrder()!=null && result.getOrder().size()>0){
					factoriesFlexTable.sortByCustom();
				}				
			}
			
			@Override
			public void onFailure(Throwable caught) {
				waitLoadCustomFactoryOrder.hide();
				handleError(caught);				
			}
		});
	}

	private void storeFactory(final Factory factory){
		dataCache.addFactory(factory);
		factoryService.addFactory(factory, new AsyncCallback<Void>() {
			public void onFailure(Throwable error) {
				handleError(error);
			}
			public void onSuccess(Void ignore) {
				statusPanel.addSuccessMessage("Fabrik erfolgreich gespeichert!");
			}
		});
	}

	private void storeProduction(final Production prod){
		//TODO: Callback
		factoryService.addProduction(prod, new AsyncCallback<Void>() {
			public void onFailure(Throwable error) {
				handleError(error);
			}
			public void onSuccess(Void ignore) {
				statusPanel.addSuccessMessage("Produktion erfolgreich gespeichert!");
			}
		});
	}
	
	private void storeCustomFactoryOrder(){
		CustomFactoryOrder order = dataCache.getCustomFactoryOrder();
		factoryService.storeCustomFactoryOrder(order, new AsyncCallback<Void>() {			
			@Override
			public void onSuccess(Void result) {
				statusPanel.addSuccessMessage("Reihenfolge erfolgreich gespeichert!");
				
			}
			
			@Override
			public void onFailure(Throwable caught) {
				handleError(caught);
				
			}
		});
	}

	private void removeFactory(Factory fac){	
		if (fac != null){			
			//Remove it from the database
			factoryService.removeFactory(fac, new AsyncCallback<Void>() {
				public void onFailure(Throwable error) {
					handleError(error);
				}
				public void onSuccess(Void ignore) {
					statusPanel.addSuccessMessage("Fabrik erfolgreich gelöscht!");
				}
			});
		}	
	}

	private void removeProduction(final Factory factory,final Production production){
		factoryService.removeProduction(production, new AsyncCallback<Void>() {
			public void onFailure(Throwable error) {
				handleError(error);
			}
			public void onSuccess(Void ignore) {
				statusPanel.addSuccessMessage("Produktion erfolgreich gelöscht!");
				factory.removeProduction(production);
				storeFactory(factory);
				dataCache.addFactory(factory);
			}
		});		
	}

	private void loadMassProductionLvl() {
		factoryService.getMassProductionLvl(new AsyncCallback<Integer>() {			
			public void onFailure(Throwable caught) {
				handleError(caught);
				waitLoadPersonalDataDialog.hide();
			}

			public void onSuccess(Integer result) {
				massProdLevel = result;
				dataCache.setMassProdLevel(massProdLevel);
				massProductionPanel.setMassProductionLvl(result);
				loadFactories();				
			}
		});
	}
	
	

	private void storeMassProduction(final int newLvl){
		factoryService.storeMassProductionLvl(newLvl, new AsyncCallback<Void>() {
			public void onFailure(Throwable error) {
				handleError(error);
			}
			public void onSuccess(Void ignore) {
				dataCache.setMassProdLevel(newLvl);
				statusPanel.addSuccessMessage("Massenproduktionslevel erfolgreich gespeichert!");
			}
		});
	}

	private void storeRecipe(final Recipe recipe){
		factoryService.storeRecipe(recipe, new AsyncCallback<Void>() {
			public void onFailure(Throwable error) {
				handleError(error);
			}
			public void onSuccess(Void ignore) {
				statusPanel.addSuccessMessage("Rezept erfolgreich gespeichert!");
				recipes.put(recipe.getName(), recipe);
				dataCache.addRecipe(recipe);
			}
		});
	}

	private Recipe getRecipeForProduct(String name){
		if (recipes.containsKey(name)){
			return recipes.get(name);
		}else{
			return null;
		}
	}

	private void handleError(Throwable error) {
		if (error instanceof NotLoggedInException) {
			Window.Location.replace(loginInfo.getLogoutUrl());
			Window.alert(error.getMessage());
			return;
		}
		Window.alert(error.getMessage());
	}
	
	private IProductClickedHandler productClickHandler = new IProductClickedHandler() {		
		public void onProductClicked(Production production, ClickEvent event) {
			Recipe r = getRecipeForProduct(production.getName());
			if (r==null){
				recipePopupPanel.showRecipePopup(production.getName(), event.getClientX()+Window.getScrollLeft(), event.getClientY()+Window.getScrollTop());
			}else{
				recipePopupPanel.showRecipePopup(r, event.getClientX()+Window.getScrollLeft(), event.getClientY()+Window.getScrollTop());
			}
			
		}
	};
	
	private IFactoryEventsHandler factoryEventsHandler = new IFactoryEventsHandler() {			
		public void onProductionRemoved(Factory factory, Production prod) {
			removeProduction(factory, prod);
			dataCache.addFactory(factory);
		}
		
		public void onProductionChanged(Production prod) {
			storeProduction(prod);
			if(!prototypes.containsKey(prod.getName())){
				prototypes.put(prod.getName(), new ProductionPrototype(prod));
			}
			dataCache.setFactories(fabs);
		}
		
		public void onFactoryRemoved(Factory factory) {
			removeFactory(factory);
			dataCache.removeFactory(factory);
		}
		
		public void onFactoryChanged(Factory factory) {
			storeFactory(factory);
			for(Production p: factory.getProductions()){
				if(!prototypes.containsKey(p.getName())){
					ProductionPrototype pt = new ProductionPrototype(p);
					prototypes.put(p.getName(), pt);
					dataCache.addPrototype(pt);
				}
			}
			dataCache.addFactory(factory);
		}
		
		public void onError(String error) {
			statusPanel.addErrorMessage(error);				
		}

		@Override
		public void onFactoryClicked(Factory factory, ClickEvent event) {
			FactoryDetailsPopupPanel popup = new FactoryDetailsPopupPanel(factory, recipes, massProdLevel);
			popup.setPopupPosition(event.getClientX()+Window.getScrollLeft(), event.getClientY()+Window.getScrollTop());
			popup.show();				
		}

		@Override
		public void onTableCollapse(boolean collapsed) {
			if (collapsed){
				overviewPanel.remove(factoriesFlexTable);
				overviewPanel.remove(rightPanel);
				factoriesFlexTable = factoriesFlexTableCollapsed;
				overviewPanel.add(factoriesFlexTable);
				overviewPanel.add(rightPanel);
				
			}else{
				overviewPanel.remove(factoriesFlexTable);
				overviewPanel.remove(rightPanel);
				factoriesFlexTable = factoriesFlexTableExpanded;
				overviewPanel.add(factoriesFlexTable);
				overviewPanel.add(rightPanel);
			}
			
		}
	};
}